package xml;

import ga.GAOptimization;
import ga.WeightScore;
import ga.WeightSet;

import java.io.*;
import java.util.PriorityQueue;

import org.w3c.dom.*;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;

public class XMLGATracker {
	// singleton
	// not parallel safe
	private static XMLGATracker instance;
	private OutputStreamWriter out;
	private InputStream in;
	private Document page;
	private Element doc;
	
	private static String file = "ga.xml";
	
	
	private XMLGATracker() {
		DocumentBuilder db = null;
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			db = dbf.newDocumentBuilder();
		} catch (Exception e) {
			System.err.println(e);
			e.printStackTrace();
		}
		
		try {
			InputStream fin = new FileInputStream(file);
			in = new BufferedInputStream(fin);
			System.out.println("Parsing file: "+file);
			page = db.parse(in);
			doc = (Element)page.getFirstChild();
			return;
			
		} catch (FileNotFoundException e) {
			// That means we haven't created the file yet
			// No worries, we can start with a blank document
			
			
		} catch (Exception e) {
			System.err.println(e);
			e.printStackTrace();
		}
		
		// if we get here, we don't have a file to parse,
		// let's create the document now
		System.out.println("Starting XMLGATracker with an empty document");
		page = db.newDocument();
		
		doc = page.createElement("GAResults");
		page.appendChild(doc);
		
	}
	
	public static XMLGATracker getInstance() {
		if (instance == null) {
			instance = new XMLGATracker();
		}
		
		return instance;
	}
	
	public void addGeneration(int generationNum, PriorityQueue<WeightScore> wsArray) {
		Text text = null;
		Element weight = null;
		Element value = null;
		Element dval = null;
		Element generation = null;
		
		generation = page.createElement("generation");
		generation.setAttribute("id", ""+generationNum);
		
		
		
		
		for(WeightScore ws: wsArray) {
			Element weightSet = page.createElement("weightSet");
			
			// append score attribute
			Element score = page.createElement("score");
			value = page.createElement("value");
			Element ival = page.createElement("int");
			text = page.createTextNode(""+ws.score);
			ival.appendChild(text);
			value.appendChild(ival);
			score.appendChild(value);
			weightSet.appendChild(score);
			generation.appendChild(weightSet);
			
			int i=1;
			for (Double d: ws.ws.w) {
				weight = page.createElement("w"+i);
				value = page.createElement("value");
				dval = page.createElement("double");
				text = page.createTextNode(""+d);
				dval.appendChild(text);
				value.appendChild(dval);
				weight.appendChild(value);
				weightSet.appendChild(weight);
				i++;
			}
			
			/*
			// w1
			weight = page.createElement("w1");
			value = page.createElement("value");
			dval = page.createElement("double");
			text = page.createTextNode(""+ws.ws.w1);
			dval.appendChild(text);
			value.appendChild(dval);
			weight.appendChild(value);
			weightSet.appendChild(weight);
			
			// w2
			weight = page.createElement("w2");
			value = page.createElement("value");
			dval = page.createElement("double");
			text = page.createTextNode(""+ws.ws.w2);
			dval.appendChild(text);
			value.appendChild(dval);
			weight.appendChild(value);
			weightSet.appendChild(weight);
			
			// w3
			weight = page.createElement("w3");
			value = page.createElement("value");
			dval = page.createElement("double");
			text = page.createTextNode(""+ws.ws.w3);
			dval.appendChild(text);
			value.appendChild(dval);
			weight.appendChild(value);
			weightSet.appendChild(weight);
			
			// w4
			weight = page.createElement("w4");
			value = page.createElement("value");
			dval = page.createElement("double");
			text = page.createTextNode(""+ws.ws.w4);
			dval.appendChild(text);
			value.appendChild(dval);
			weight.appendChild(value);
			weightSet.appendChild(weight);
			*/
			










			

		}
		
		doc.appendChild(generation);
	}
	
	public double[] getBestWeightSet() {
		NodeList wss = page.getElementsByTagName("weightSet");
		if (wss.getLength() <= 0) return null;
		
		int bestScore = Integer.MAX_VALUE;
		double[] weightSet = new double[GAOptimization.nVars+1];
		
		for(int i=0;i<wss.getLength();i++) {
			Node n = wss.item(i);
			String v = ((Element)n).getElementsByTagName("int").item(0).getFirstChild().getNodeValue();
			int score = Integer.parseInt(v);
			if (score < bestScore) {
				weightSet[0] = score;
				NodeList doubles = ((Element)n).getElementsByTagName("double");
				for(int j=0; j<doubles.getLength(); j++) {
					String d = doubles.item(j).getParentNode().getParentNode().getNodeName();
					int index = Integer.parseInt(""+d.charAt(1));
					weightSet[index] = Double.parseDouble( doubles.item(j).getFirstChild().getNodeValue() );
				}
				bestScore = score;
			}
			
		}
		
		return weightSet;
	}
	
	public int getOldestGeneration() {
		NodeList gid = page.getElementsByTagName("generation");
		int oldest = 0;
		
		int id=0;
		for(int i=0; i<gid.getLength(); i++) {
			Node n = gid.item(i);
			id = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
			if (id>oldest) oldest = id;
		}
		
		return oldest;
	}
	
	public PriorityQueue<WeightScore> getPopulationAtGeneration(int gen) {
		PriorityQueue<WeightScore> pq = new PriorityQueue<WeightScore>();
		
		NodeList genList = page.getElementsByTagName("generation");
		
		Node genPop=null;
		
		for (int i=0; i<genList.getLength(); i++) {
			int gid = Integer.parseInt(genList.item(i).getAttributes().getNamedItem("id").getNodeValue());
			if (gid == gen) {
				genPop = genList.item(i);
				break;
			}
			
		}
		
		int score=0;
		double[] weightSet = new double[GAOptimization.nVars];
		
		NodeList wss = ((Element)genPop).getElementsByTagName("weightSet");
		for (int i=0; i<wss.getLength(); i++) {
			score = Integer.parseInt(((Element)wss.item(i)).getElementsByTagName("int").item(0).getFirstChild().getNodeValue());
			NodeList doubles = ((Element)wss.item(i)).getElementsByTagName("double");
			for(int j=0; j<doubles.getLength(); j++) {
				String d = doubles.item(j).getParentNode().getParentNode().getNodeName();
				int index = Integer.parseInt(""+d.charAt(1));
				weightSet[index-1] = Double.parseDouble( doubles.item(j).getFirstChild().getNodeValue() );
			}
			
			pq.add(new WeightScore(score, new WeightSet(weightSet[0],weightSet[1],weightSet[2],weightSet[3],weightSet[4],weightSet[5])));
		}
		
		return pq;
	}
	
	public void writeXML() {
		try {
			// we don't want to append to this file
			// we want to write out the new document tree each time
			OutputStream fout = new FileOutputStream(file);
			OutputStream bout = new BufferedOutputStream(fout);
			out = new OutputStreamWriter(bout, "8859_1");
			
			
			TransformerFactory xformFactory = TransformerFactory.newInstance();
			Transformer idTransform = xformFactory.newTransformer();
			Source input = new DOMSource(doc);
			Result output = new StreamResult(out);
			idTransform.transform(input,output);
			out.flush();
			out.close();
			
		} catch (Exception e) {
			System.err.println(e);
			e.printStackTrace();
			
		}
		
	}
	
	public static void main(String[] args) {
		//PriorityQueue<WeightScore> pq = new PriorityQueue<WeightScore>();
		//pq.add(new WeightScore(5,new WeightSet(1,2,3,4)));
		//pq.add(new WeightScore(2,new WeightSet(2,3,4,5)));
		
		//XMLGATracker.getInstance().addGeneration(0, pq);
		//XMLGATracker.getInstance().writeXML();
		//XMLGATracker.getInstance().addGeneration(1,pq);
		//XMLGATracker.getInstance().writeXML();
		
		XMLGATracker t = XMLGATracker.getInstance();
		double[] best = t.getBestWeightSet();
		int gen = t.getOldestGeneration();
		PriorityQueue<WeightScore> pq = t.getPopulationAtGeneration(gen);
		
		System.out.println("weightSet["+gen+"]:"+"\nscore: "+best[0]+"\nw1: "+best[1]+"\nw2: "+best[2]+"\nw3: "+best[3]+"\nw4: "+best[4]+"\nw5: "+best[5]+"\nw6: "+best[6]+"\nw7: "+best[7]);
		for(WeightScore ws: pq) {
			System.out.println(ws);
		}
	}
	
}
